package rwt.server.railroad;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.*;

import rwt.server.RailServer;
import rwt.server.RailroadMessages.GetSwitchStatesMsg;
import rwt.server.WebMessages.PutSwitches;

public class SwitchManager 
{
	private static final Logger logger = LoggerFactory.getLogger(SwitchManager.class);
	
	private final int SWITCH_CLOSED = 0;
	private final int SWITCH_THROWN = 1;
	private final int SWITCH_BEGIN_CLOSED = 2;
	private final int SWITCH_BEGIN_THROWN = 3;
	private final int SWITCH_UNKNOWN = 4;
	
	private static boolean isCreated = false;

	private ConcurrentMap<Integer, SwitchStatus> switchMap = new ConcurrentHashMap<>();
	private List<Integer> sortedSwitchIds = new ArrayList<>();
	
	public static SwitchManager create()
	{
		if (isCreated)
			throw new RuntimeException("SwitchManager has already been created.");
		isCreated = true;
		return new SwitchManager();
	}

	private SwitchManager()
	{
	}
	
	//Adds a switch as unknown
	public void SetSwitch(int switchId)
	{
		if (!switchMap.containsKey(switchId))
			SetSwitch(switchId, SWITCH_UNKNOWN);
	}
	
	//Set state of a Switch
	public void SetSwitch(int switchId, int railState)
	{
		SwitchStatus state = SwitchStatus.Unknown;
		switch (railState)
		{
		case SWITCH_CLOSED:
			state = SwitchStatus.Closed;
			break;
		case SWITCH_THROWN:
			state = SwitchStatus.Thrown;
			break;
		case SWITCH_BEGIN_CLOSED:
			state = SwitchStatus.BeginClosed;
			break;
		case SWITCH_BEGIN_THROWN:
			state = SwitchStatus.BeginThrown;
			break;
		case SWITCH_UNKNOWN:
			state = SwitchStatus.Unknown;
			break;
		}
		if (!switchMap.containsKey(switchId))
		{
			sortedSwitchIds.add(switchId);
			Collections.sort(sortedSwitchIds);
		}
		switchMap.put(switchId, state);
		logger.debug("Switch " +  switchId + " " + switchMap.get(switchId));
	}

	public List<Integer> getSortedSwitchIds() 
	{
		return sortedSwitchIds;
	}
	
	public Map<Integer, SwitchStatus> getSwitchMap()
	{
		return switchMap;
	}
	
	public SwitchStatus getSwitchState(int switchId)
	{
		if (switchMap.containsKey(switchId))
			return switchMap.get(switchId);
		return SwitchStatus.Unknown;
	}
	
	public void getSwitchStates()
	{
		GetSwitchStatesMsg msg  = new GetSwitchStatesMsg();
		RailServer.getController().write(msg);
	}
	
	// Returns the switch state to be sent to the controller
	public int changeSwitchState(int switchId)
	{
		switch (switchMap.get(switchId))
		{
		case BeginClosed:
		case Closed:
			return SWITCH_THROWN;
		case Thrown:
		case BeginThrown:
			return SWITCH_CLOSED;
		case Unknown:
			break;
		default:
			break;
		
		}
		return SWITCH_UNKNOWN;
	}

	public void reset() {
		for (int key : switchMap.keySet())
		{
			switchMap.put(key, SwitchStatus.Unknown);
		}
		PutSwitches msg = new PutSwitches(switchMap);
		Switches.getInstance().sendToAll(msg);
	}
}
